﻿using System;
using System.Collections.Generic;
using System.IO;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using advadev.DataStorage;


namespace advadev.DataStorage.UnitTests
{

    /// <summary>
    /// Represents the xml file data storage unit tests
    /// </summary>
    [TestClass]
    public class XmlFileDataStorageTest
    {

        /// <summary>for internal use</summary>
        static private string _applicationKey = "advadev.DataStorage.UnitTests.XmlFileDataStorageTest";


        #region save tests
        /// <summary>
        /// Save single object test
        /// </summary>
        [TestMethod]
        public void SaveSingleObjectTest()
        {
            // initilization
            string fileName = "..\\..\\..\\advadev.DataStorage.UnitTests\\DataStorageFiles\\Temporary.xml";
            GenericTestObject genericObject = new GenericTestObject(1, "item " + 1);
            AdditionalGenericTestObject additionalGenericObject = new AdditionalGenericTestObject(1, "item " + 1);
            UnserializableGenericTestObject invalidGenericObject = new UnserializableGenericTestObject(1, "item " + 1);
            GenericTestObject nullGenericObject = null;
            GenericTestObject referenceObject = null;
            AdditionalGenericTestObject additionalReferenceObject = null;
            XmlFileDataStorage testObject = new XmlFileDataStorage(_applicationKey, fileName);

            // test with valid new object
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(genericObject);
            Assert.AreEqual(1, testObject.Select<GenericTestObject>().Count);
            referenceObject = testObject.Select<GenericTestObject>()[0];
            Assert.AreEqual(genericObject.Id, referenceObject.Id);
            Assert.AreEqual(genericObject.Name, referenceObject.Name);
            testObject.DeleteAll();
            deleteFile(fileName);

            // test with valid existing object
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(genericObject);
            genericObject = new GenericTestObject(1, "item " + 2);
            testObject.Save<GenericTestObject>(genericObject);
            Assert.AreEqual(1, testObject.Select<GenericTestObject>().Count);
            referenceObject = testObject.Select<GenericTestObject>()[0];
            Assert.AreEqual(genericObject.Id, referenceObject.Id);
            Assert.AreEqual(genericObject.Name, referenceObject.Name);
            genericObject = new GenericTestObject(1, "item " + 1);
            testObject.DeleteAll();
            deleteFile(fileName);

            // test with different types with same id
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(genericObject);
            testObject.Save<AdditionalGenericTestObject>(additionalGenericObject);
            Assert.AreEqual(1, testObject.Select<GenericTestObject>().Count);
            referenceObject = testObject.Select<GenericTestObject>()[0];
            Assert.AreEqual(genericObject.Id, referenceObject.Id);
            Assert.AreEqual(genericObject.Name, referenceObject.Name);
            Assert.AreEqual(1, testObject.Select<AdditionalGenericTestObject>().Count);
            additionalReferenceObject = testObject.Select<AdditionalGenericTestObject>()[0];
            Assert.AreEqual(additionalGenericObject.Id, additionalReferenceObject.Id);
            Assert.AreEqual(additionalGenericObject.Name, additionalReferenceObject.Name);
            testObject.DeleteAll();
            deleteFile(fileName);

            // test with changed serialization
            deleteFile(fileName);
            testObject.DeleteAll();
            genericObject.ChangedXmlSerialization = true;
            try
            {
                testObject.Save<GenericTestObject>(genericObject);
                Assert.Fail("Exception should be thrown if a generic object is not xml de-serializable");
            }
            catch { }
            genericObject.ChangedXmlSerialization = false;
            testObject.DeleteAll();
            deleteFile(fileName);

            // test with null object
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(nullGenericObject);
            Assert.AreEqual(0, testObject.Select<GenericTestObject>().Count);
            testObject.DeleteAll();
            deleteFile(fileName);

            // test with invalid object
            deleteFile(fileName);
            testObject.DeleteAll();
            try
            {
                testObject.Save<UnserializableGenericTestObject>(invalidGenericObject);
                Assert.Fail("Exception should be thrown if a generic object is not xml serializable");
            }
            catch { }
            Assert.AreEqual(0, testObject.Select<UnserializableGenericTestObject>().Count);
            testObject.DeleteAll();
            deleteFile(fileName);
        }

        /// <summary>
        /// Save list test
        /// </summary>
        [TestMethod]
        public void SaveListTest()
        {
            // initilization
            string fileName = "..\\..\\..\\advadev.DataStorage.UnitTests\\DataStorageFiles\\Temporary.xml";
            GenericTestObject genericObject1 = new GenericTestObject(1, "item " + 1);
            GenericTestObject genericObject2 = new GenericTestObject(2, "item " + 2);
            GenericTestObject genericObject3 = new GenericTestObject(3, "item " + 3);
            AdditionalGenericTestObject additionalGenericObject = new AdditionalGenericTestObject(1, "item " + 1);
            UnserializableGenericTestObject invalidGenericObject = new UnserializableGenericTestObject(1, "item " + 1);
            GenericTestObject nullGenericObject = null;
            GenericTestObject referenceObject = null;
            AdditionalGenericTestObject additionalReferenceObject = null;
            XmlFileDataStorage testObject = new XmlFileDataStorage(_applicationKey, fileName);

            // test with valid new objects
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(new List<GenericTestObject>() { genericObject1, genericObject2, genericObject3 });
            Assert.AreEqual(3, testObject.Select<GenericTestObject>().Count);
            referenceObject = testObject.Select<GenericTestObject>()[0];
            Assert.AreEqual(genericObject1.Id, referenceObject.Id);
            Assert.AreEqual(genericObject1.Name, referenceObject.Name);
            referenceObject = testObject.Select<GenericTestObject>()[1];
            Assert.AreEqual(genericObject2.Id, referenceObject.Id);
            Assert.AreEqual(genericObject2.Name, referenceObject.Name);
            referenceObject = testObject.Select<GenericTestObject>()[2];
            Assert.AreEqual(genericObject3.Id, referenceObject.Id);
            Assert.AreEqual(genericObject3.Name, referenceObject.Name);
            testObject.DeleteAll();
            deleteFile(fileName);

            // test width valid existing object
            deleteFile(fileName);
            testObject.DeleteAll();
            genericObject2 = new GenericTestObject(1, "item " + 2);
            testObject.Save<GenericTestObject>(new List<GenericTestObject>() { genericObject1, genericObject2, genericObject3 });
            Assert.AreEqual(2, testObject.Select<GenericTestObject>().Count);
            referenceObject = testObject.Select<GenericTestObject>()[0];
            Assert.AreEqual(genericObject2.Id, referenceObject.Id);
            Assert.AreEqual(genericObject2.Name, referenceObject.Name);
            referenceObject = testObject.Select<GenericTestObject>()[1];
            Assert.AreEqual(genericObject3.Id, referenceObject.Id);
            Assert.AreEqual(genericObject3.Name, referenceObject.Name);
            genericObject2 = new GenericTestObject(2, "item " + 2);
            testObject.DeleteAll();
            deleteFile(fileName);

            // test with different types with same id
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(new List<GenericTestObject>() { genericObject1, genericObject2, genericObject3 });
            testObject.Save<AdditionalGenericTestObject>(new List<AdditionalGenericTestObject>() { additionalGenericObject });
            Assert.AreEqual(3, testObject.Select<GenericTestObject>().Count);
            referenceObject = testObject.Select<GenericTestObject>()[0];
            Assert.AreEqual(genericObject1.Id, referenceObject.Id);
            Assert.AreEqual(genericObject1.Name, referenceObject.Name);
            referenceObject = testObject.Select<GenericTestObject>()[1];
            Assert.AreEqual(genericObject2.Id, referenceObject.Id);
            Assert.AreEqual(genericObject2.Name, referenceObject.Name);
            referenceObject = testObject.Select<GenericTestObject>()[2];
            Assert.AreEqual(genericObject3.Id, referenceObject.Id);
            Assert.AreEqual(genericObject3.Name, referenceObject.Name);
            Assert.AreEqual(1, testObject.Select<AdditionalGenericTestObject>().Count);
            additionalReferenceObject = testObject.Select<AdditionalGenericTestObject>()[0];
            Assert.AreEqual(additionalGenericObject.Id, additionalReferenceObject.Id);
            Assert.AreEqual(additionalGenericObject.Name, additionalReferenceObject.Name);
            testObject.DeleteAll();
            deleteFile(fileName);

            // test with null object
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(new List<GenericTestObject>() { genericObject1, nullGenericObject, genericObject3 });
            Assert.AreEqual(2, testObject.Select<GenericTestObject>().Count);
            referenceObject = testObject.Select<GenericTestObject>()[0];
            Assert.AreEqual(genericObject1.Id, referenceObject.Id);
            Assert.AreEqual(genericObject1.Name, referenceObject.Name);
            referenceObject = testObject.Select<GenericTestObject>()[1];
            Assert.AreEqual(genericObject3.Id, referenceObject.Id);
            Assert.AreEqual(genericObject3.Name, referenceObject.Name);
            testObject.DeleteAll();
            deleteFile(fileName);

            // test with null list
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>((List<GenericTestObject>)null);
            Assert.AreEqual(0, testObject.Select<GenericTestObject>().Count);
            testObject.Delete<GenericTestObject>();
            deleteFile(fileName);

            // test with invalid object
            deleteFile(fileName);
            testObject.Delete<GenericTestObject>();
            try
            {
                testObject.Save<UnserializableGenericTestObject>(new List<UnserializableGenericTestObject>() { invalidGenericObject });
                Assert.Fail("Exception should be thrown if a generic object is not xml serializable");
            }
            catch { }
            Assert.AreEqual(0, testObject.Select<UnserializableGenericTestObject>().Count);
            testObject.DeleteAll();
            deleteFile(fileName);
        }
        #endregion save tests


        #region find tests
        /// <summary>
        /// Find by id test
        /// </summary>
        [TestMethod]
        public void FindByIdTest()
        {
            // initilization
            string fileName = "..\\..\\..\\advadev.DataStorage.UnitTests\\DataStorageFiles\\Temporary.xml";
            GenericTestObject genericObject1 = new GenericTestObject(1, "item " + 1);
            GenericTestObject genericObject2 = new GenericTestObject(2, "item " + 2);
            GenericTestObject genericObject3 = new GenericTestObject(3, "item " + 3);
            GenericTestObject referenceObject = null;
            XmlFileDataStorage testObject = new XmlFileDataStorage(_applicationKey, fileName);

            // test with existing object
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(new List<GenericTestObject>() { genericObject1, genericObject2, genericObject3 });
            Assert.AreEqual(3, testObject.Select<GenericTestObject>().Count);
            referenceObject = testObject.Find<GenericTestObject>("2");
            Assert.AreEqual(genericObject2.Id, referenceObject.Id);
            Assert.AreEqual(genericObject2.Name, referenceObject.Name);
            testObject.DeleteAll();
            deleteFile(fileName);

            // test with not existing object
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(new List<GenericTestObject>() { genericObject1, genericObject2, genericObject3 });
            Assert.AreEqual(3, testObject.Select<GenericTestObject>().Count);
            referenceObject = testObject.Find<GenericTestObject>("5");
            Assert.IsNull(referenceObject);
            testObject.DeleteAll();
            deleteFile(fileName);
        }
        #endregion find tests


        #region select tests
        /// <summary>
        /// Select all test
        /// </summary>
        [TestMethod]
        public void SelectAllTest()
        {
            // initilization
            string fileName = "..\\..\\..\\advadev.DataStorage.UnitTests\\DataStorageFiles\\Temporary.xml";
            GenericTestObject genericObject1 = new GenericTestObject(1, "item " + 1);
            GenericTestObject genericObject2 = new GenericTestObject(2, "item " + 2);
            GenericTestObject genericObject3 = new GenericTestObject(3, "item " + 3);
            GenericTestObject referenceObject = null;
            XmlFileDataStorage testObject = new XmlFileDataStorage(_applicationKey, fileName);

            // select with existing objects
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(new List<GenericTestObject>() { genericObject1, genericObject2, genericObject3 });
            Assert.AreEqual(3, testObject.Select<GenericTestObject>().Count);
            referenceObject = testObject.Select<GenericTestObject>()[0];
            Assert.AreEqual(genericObject1.Id, referenceObject.Id);
            Assert.AreEqual(genericObject1.Name, referenceObject.Name);
            referenceObject = testObject.Select<GenericTestObject>()[1];
            Assert.AreEqual(genericObject2.Id, referenceObject.Id);
            Assert.AreEqual(genericObject2.Name, referenceObject.Name);
            referenceObject = testObject.Select<GenericTestObject>()[2];
            Assert.AreEqual(genericObject3.Id, referenceObject.Id);
            Assert.AreEqual(genericObject3.Name, referenceObject.Name);
            testObject.DeleteAll();
            deleteFile(fileName);

            // select with empty data storage
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(new List<GenericTestObject>() { });
            Assert.AreEqual(0, testObject.Select<GenericTestObject>().Count);
            testObject.DeleteAll();
            deleteFile(fileName);
        }
        #endregion select tests


        #region delete tests
        /// <summary>
        /// Delete single object test
        /// </summary>
        [TestMethod]
        public void DeleteSingleObjectTest()
        {
            // initilization
            string fileName = "..\\..\\..\\advadev.DataStorage.UnitTests\\DataStorageFiles\\Temporary.xml";
            GenericTestObject genericObject = new GenericTestObject(1, "item " + 1);
            GenericTestObject nullGenericObject = null;
            XmlFileDataStorage testObject = new XmlFileDataStorage(_applicationKey, fileName);

            // test with existing object
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(genericObject);
            Assert.AreEqual(1, testObject.Select<GenericTestObject>().Count);
            genericObject = new GenericTestObject(1, "NEWNAME");
            testObject.Delete<GenericTestObject>(genericObject);
            Assert.AreEqual(0, testObject.Select<GenericTestObject>().Count);
            genericObject = new GenericTestObject(1, "item " + 1);
            testObject.DeleteAll();
            deleteFile(fileName);

            // test with not existing object
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(genericObject);
            Assert.AreEqual(1, testObject.Select<GenericTestObject>().Count);
            genericObject = new GenericTestObject(2, "item" + 1);
            testObject.Delete<GenericTestObject>(genericObject);
            Assert.AreEqual(1, testObject.Select<GenericTestObject>().Count);
            genericObject = new GenericTestObject(1, "item " + 1);
            testObject.DeleteAll();
            deleteFile(fileName);

            // test with null object
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(genericObject);
            Assert.AreEqual(1, testObject.Select<GenericTestObject>().Count);
            testObject.Delete<GenericTestObject>(nullGenericObject);
            Assert.AreEqual(1, testObject.Select<GenericTestObject>().Count);
            testObject.DeleteAll();
            deleteFile(fileName);
        }

        /// <summary>
        /// Delete list test
        /// </summary>
        [TestMethod]
        public void DeleteListTest()
        {
            // initilization
            string fileName = "..\\..\\..\\advadev.DataStorage.UnitTests\\DataStorageFiles\\Temporary.xml";
            GenericTestObject genericObject1 = new GenericTestObject(1, "item " + 1);
            GenericTestObject genericObject2 = new GenericTestObject(2, "item " + 2);
            GenericTestObject genericObject3 = new GenericTestObject(3, "item " + 3);
            GenericTestObject nullGenericObject = null;
            GenericTestObject referenceObject = null;
            XmlFileDataStorage testObject = new XmlFileDataStorage(_applicationKey, fileName);

            // test with existing object
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(new List<GenericTestObject>() { genericObject1, genericObject2, genericObject3 });
            Assert.AreEqual(3, testObject.Select<GenericTestObject>().Count);
            genericObject1 = new GenericTestObject(1, "NEWNAME");
            genericObject3 = new GenericTestObject(3, "NEWNAME");
            testObject.Delete<GenericTestObject>(new List<GenericTestObject>() { genericObject1, genericObject3 });
            Assert.AreEqual(1, testObject.Select<GenericTestObject>().Count);
            referenceObject = testObject.Select<GenericTestObject>()[0];
            Assert.AreEqual(genericObject2.Id, referenceObject.Id);
            Assert.AreEqual(genericObject2.Name, referenceObject.Name);
            genericObject1 = new GenericTestObject(1, "item " + 1);
            genericObject3 = new GenericTestObject(3, "item " + 3);
            testObject.DeleteAll();
            deleteFile(fileName);

            // test with not existing object
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(new List<GenericTestObject>() { genericObject1, genericObject2, genericObject3 });
            Assert.AreEqual(3, testObject.Select<GenericTestObject>().Count);
            genericObject1 = new GenericTestObject(5, "item " + 1);
            testObject.Delete<GenericTestObject>(new List<GenericTestObject>() { genericObject1, genericObject3 });
            Assert.AreEqual(2, testObject.Select<GenericTestObject>().Count);
            referenceObject = testObject.Select<GenericTestObject>()[0];
            Assert.AreEqual(1, referenceObject.Id);
            Assert.AreEqual(genericObject1.Name, referenceObject.Name);
            referenceObject = testObject.Select<GenericTestObject>()[1];
            Assert.AreEqual(genericObject2.Id, referenceObject.Id);
            Assert.AreEqual(genericObject2.Name, referenceObject.Name);
            genericObject1 = new GenericTestObject(1, "item " + 1);
            testObject.DeleteAll();
            deleteFile(fileName);

            // test with null object
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(new List<GenericTestObject>() { genericObject1, genericObject2, genericObject3 });
            Assert.AreEqual(3, testObject.Select<GenericTestObject>().Count);
            testObject.Delete<GenericTestObject>(new List<GenericTestObject>() { nullGenericObject, genericObject3 });
            Assert.AreEqual(2, testObject.Select<GenericTestObject>().Count);
            referenceObject = testObject.Select<GenericTestObject>()[0];
            Assert.AreEqual(genericObject1.Id, referenceObject.Id);
            Assert.AreEqual(genericObject1.Name, referenceObject.Name);
            referenceObject = testObject.Select<GenericTestObject>()[1];
            Assert.AreEqual(genericObject2.Id, referenceObject.Id);
            Assert.AreEqual(genericObject2.Name, referenceObject.Name);
            testObject.DeleteAll();
            deleteFile(fileName);

            // test with empty list
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(new List<GenericTestObject>() { genericObject1, genericObject2, genericObject3 });
            Assert.AreEqual(3, testObject.Select<GenericTestObject>().Count);
            testObject.Delete<GenericTestObject>(new List<GenericTestObject>() { });
            Assert.AreEqual(3, testObject.Select<GenericTestObject>().Count);
            testObject.DeleteAll();
            deleteFile(fileName);

            // test with null list
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(new List<GenericTestObject>() { genericObject1, genericObject2, genericObject3 });
            Assert.AreEqual(3, testObject.Select<GenericTestObject>().Count);
            testObject.Delete<GenericTestObject>((List<GenericTestObject>)null);
            Assert.AreEqual(3, testObject.Select<GenericTestObject>().Count);
            testObject.DeleteAll();
            deleteFile(fileName);
        }

        /// <summary>
        /// Delete all of type test
        /// </summary>
        [TestMethod]
        public void DeleteAllOfTypeTest()
        {
            // initilization
            string fileName = "..\\..\\..\\advadev.DataStorage.UnitTests\\DataStorageFiles\\Temporary.xml";
            GenericTestObject genericObject1 = new GenericTestObject(1, "item " + 1);
            GenericTestObject genericObject2 = new GenericTestObject(2, "item " + 2);
            GenericTestObject genericObject3 = new GenericTestObject(3, "item " + 3);
            AdditionalGenericTestObject additionalGenericObject = new AdditionalGenericTestObject(1, "item " + 1);
            XmlFileDataStorage testObject = new XmlFileDataStorage(_applicationKey, fileName);

            // test with existing objects
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(new List<GenericTestObject>() { genericObject1, genericObject2, genericObject3 });
            testObject.Save<AdditionalGenericTestObject>(additionalGenericObject);
            testObject.Delete<GenericTestObject>();
            Assert.AreEqual(0, testObject.Select<GenericTestObject>().Count);
            Assert.AreEqual(1, testObject.Select<AdditionalGenericTestObject>().Count);
            testObject.DeleteAll();
            deleteFile(fileName);

            // test with empty list
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(new List<GenericTestObject>() { });
            testObject.Delete<GenericTestObject>();
            Assert.AreEqual(0, testObject.Select<GenericTestObject>().Count);
            testObject.DeleteAll();
            deleteFile(fileName);
        }

        /// <summary>
        /// Delete all of application test
        /// </summary>
        [TestMethod]
        public void DeleteAllOfApplicationTest()
        {
            // initilization
            string fileName = "..\\..\\..\\advadev.DataStorage.UnitTests\\DataStorageFiles\\Temporary.xml";
            GenericTestObject genericObject1 = new GenericTestObject(1, "item " + 1);
            GenericTestObject genericObject2 = new GenericTestObject(2, "item " + 2);
            GenericTestObject genericObject3 = new GenericTestObject(3, "item " + 3);
            AdditionalGenericTestObject additionalGenericObject = new AdditionalGenericTestObject(1, "item " + 1);
            XmlFileDataStorage testObject = new XmlFileDataStorage(_applicationKey, fileName);

            // test with existing objects
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(new List<GenericTestObject>() { genericObject1, genericObject2, genericObject3 });
            testObject.Save<AdditionalGenericTestObject>(additionalGenericObject);
            testObject.DeleteAll();
            Assert.AreEqual(0, testObject.Select<GenericTestObject>().Count);
            Assert.AreEqual(0, testObject.Select<AdditionalGenericTestObject>().Count);
            testObject.DeleteAll();
            deleteFile(fileName);

            // test with empty list
            deleteFile(fileName);
            testObject.DeleteAll();
            testObject.Save<GenericTestObject>(new List<GenericTestObject>() { });
            testObject.DeleteAll();
            Assert.AreEqual(0, testObject.Select<GenericTestObject>().Count);
            testObject.DeleteAll();
            deleteFile(fileName);
        }
        #endregion delete tests


        /// <summary>
        /// Deletes the file with the given name
        /// </summary>
        /// <param name="fileName">
        /// File name
        /// </param>
        private void deleteFile(string fileName)
        {
            if (File.Exists(fileName))
            {
                File.Delete(fileName);
                if (File.Exists(fileName))
                {
                    Assert.Fail("Unable to delete existing file");
                }
            }
        }

    }

}
